'''
Created on Feb 11, 2011

@author: Jonathan Herbst
'''

import time, urllib2, re
from threading import Thread, Event
from xml.dom import minidom

from Utilities import ThreadedConfigSynchronizer, LogFile
from SQSReceiver import SQSReceiver, AWSAuthentication
from GoogleC2DM import GoogleC2DM, GoogleClientLogin
import Messages

class Configuration():
    
    _attributeSections = \
        {
         'iPAddress': "Device",
         'logDir': "Device",
         'debug': "Device",
         'networkValidate': "Device",
         'bluetoothEnable': "Device",
         'gpioEnable': "Device",
         'gpioDirectory': "Device",
         'doorLatchGPIO': "Device",
         'motionSensorGPIO': "Device",
         'doorBellGPIO': "Device",
         'btResetGPIO': "Device",
         'btResetLEDGPIO': "Device",
         'redLEDGPIO': "Device",
         'yellowLEDGPIO': "Device",
         'greenLEDGPIO': "Device",
         'motionDetectedLEDGPIO': "Device",
         'aWSAccessKey': "Amazon Web Services",
         'aWSSecretKey': "Amazon Web Services",
         'sQSQueueName': "Amazon Web Services",
         'c2DMRegistrationId': "Google C2DM",
         'googleUsername': "Google C2DM",
         'googlePassword': "Google C2DM",
         'bluetoothPower': "Bluetooth",
         'bluetoothUUID': "Bluetooth",
         'bluetoothPassKey': "Bluetooth",
         'bluetoothPinCode': "Bluetooth",
         'cameraPort': "Camera",
         'androidKey': "Android",
         'pcAppUserName': "PCApp",
         'pcAppPassword': "PCApp",
         'pcAppPort': "PCApp",
         'pcAppClientPort': "PCApp",
         'pcAppSession': "PCApp",
         }
    
    def __init__(self, configFile):
        self.m_configSynchronizer = ThreadedConfigSynchronizer(configFile)
        
    def Exit(self):
        self.m_configSynchronizer.Exit()
        
    def __getattr__(self, name):
        if name in self._attributeSections:
            section = self._attributeSections[name]
            return self.m_configSynchronizer.Get(section, name)
        raise AttributeError(name)
    
    def __setattr__(self, name, value):
        if name in self._attributeSections:
            section = self._attributeSections[name]
            attempt = self.m_configSynchronizer.Set(section, name, value)
            if not attempt:
                self.m_configSynchronizer.AddSection(section)
                self.m_configSynchronizer.Set(section, name, value)
        else:
            self.__dict__[name] = value
            
class DoorAlertException(Exception):
    pass

class MissingGoogleCredentialsException(DoorAlertException):
    pass

class MissingAWSCredentialsException(DoorAlertException):
    pass

def SendC2DMMessage(config, message):
    # C2DM credentials can change so we want to relogin every time
    if not config.googleUsername or not config.googlePassword:
        raise MissingGoogleCredentialsException()
    if config.c2DMRegistrationId:
        c2DM = GoogleC2DM(config.googleUsername,config.googlePassword, config.c2DMRegistrationId)
        return c2DM.SendMessage(message)
    return False
            
class DoorAlert(Thread):
    
    def __init__(self, config, controlStructure):
        self.m_config = config
        self.m_controlStructure = controlStructure
        self.m_log = LogFile("DoorAlert")
        
        self._SetupAWS()
        
        self.t_exit = Event()
        
        Thread.__init__(self)
        self.m_running = True
        self.start()
        
    def _SetupAWS(self):
        if not self.m_config.aWSAccessKey or not self.m_config.aWSSecretKey:
            raise MissingAWSCredentialsException()
        self.m_aWSAuthentication = AWSAuthentication(self.m_config.aWSAccessKey, self.m_config.aWSSecretKey)
        if not self.m_config.sQSQueueName:
            self.m_config.sQSQueueName = "myqueue"
        self.m_sQSReceiver = SQSReceiver(self.m_config.sQSQueueName, self.m_aWSAuthentication)
    
    def Abort(self):
        self.t_exit.set()
    
    def IsRunning(self):
        return self.m_running
    
    def run(self):
        self.m_log.Write("Door Alert", "Started")
        self._C2DMRegistrationIdFromSQS()
        GetIPAddress(self.m_config)
        if self.m_config.iPAddress is None:
            self.m_log.Write("Get IP Address", "No IP Address", LogFile.FATAL)
            return
        responseReceived = False
        if self._SendAlert():
            try:
                responseReceived = self._GetSQSResponse()
            except:
                pass
        if not responseReceived:
            self.m_controlStructure.Push(Messages.SQSDoorCommand(Messages.DoorCommand("0", Messages.DoorCommand.DENYACCESSACTION)))
        self.m_log.Write("Door Alert", "Ended")
        self.m_running = False
    
    def _SendAlert(self):
        alertSuccess = False
        self.m_log.Write("Send Alert", "Started")
        try:
            alertMessage = Messages.EntrantDetected(self.m_config.iPAddress, self.m_config.cameraPort, self.m_config.sQSQueueName)
        except:
            self.m_log.Write("Send Alert", "Unable to construct Entrant Detected message, invalid parameters", LogFile.FATAL)
            return False
        try:
            alertSuccess = SendC2DMMessage(self.m_config, alertMessage.renderMessage())
        except Exception, e:
            self.m_log.Write("Send Alert", str(e), LogFile.ERROR)
            return False
        if not alertSuccess:
            self.m_log.Write("Send Alert", "Timout", LogFile.WARNING)
        self.m_log.Write("Send Alert", "Finished")
        return alertSuccess
    
    def _C2DMRegistrationIdFromSQS(self):
        self.m_log.Write("Retrieve C2DM Redistration Id", "Started")
        c2DMRegistrationMessage = None
        for message in self.m_sQSReceiver.ReceiveMessages():
            parsedMessage = Messages.parseMessage(message)
            if parsedMessage.__class__ is Messages.C2DMRegistration:
                c2DMRegistrationMessage = parsedMessage
        self.m_log.Write("Retrieve C2DM Redistration Id", "Here")
        if c2DMRegistrationMessage is not None:
            self.m_config.c2DMRegistrationId = c2DMRegistrationMessage.RegistrationId()
            self.m_c2DM = GoogleC2DM(self.m_config.googleUsername, self.m_config.googlePassword, self.m_config.c2DMRegistrationId)
            self.m_log.Write("Retrieve C2DM Redistration Id", "Finished: Registration Id found")
            self.m_log.Write("Retrieve C2DM Redistration Id", "Registration Id: " + self.m_config.c2DMRegistrationId, LogFile.DEBUG)
            return True
        self.m_log.Write("Retrieve C2DM Redistration Id", "Registration Id not found", LogFile.WARNING)
        return False
    
    def _GetSQSResponse(self, timeout=60):
        """
        Retrieve messages from the SQS queue until a timeout is reached
        
        Parameters:
        timeout - the number of times to poll SQS before stopping
        """
        for i in range(timeout):
            
            if self.t_exit.isSet():
                return True
            
            # get all messages from Amazon SQS
            self.m_log.Write("Get SQS Response", "Polling SQS", LogFile.DEBUG)
            for message in self.m_sQSReceiver.ReceiveMessages():
                parsedMessage = Messages.parseMessage(message)
                if parsedMessage is None:
                    pass    # unrecognized message, do nothing
                elif parsedMessage.__class__ is Messages.DoorCommand:
                    self.m_controlStructure.Push(Messages.SQSDoorCommand(parsedMessage))
                elif parsedMessage.__class__ is Messages.C2DMRegistration:
                    self.m_controlStructure.Push(parsedMessage)
                # ignore if it's any other kind of message
            
            self.t_exit.wait(1)
        return False

_iPAddressWebSite = "http://www.whatismyip.com/automation/n09230945.asp"
_iPAddressRE = re.compile("\d+\.\d+\.\d+\.\d+")
def GetIPAddress(config):
    try:
        request = urllib2.Request(_iPAddressWebSite)
        response = urllib2.urlopen(request, timeout=5)
        iPAddress = str(response.read()).strip()
        if _iPAddressRE.match(iPAddress) is not None:
            config.iPAddress = iPAddress
        return config.iPAddress
    except:
        return None

def test_getIPAddress():
    config = Configuration("config.ini")
    try:
        GetIPAddress(config)
        print config.iPAddress
    except Exception, e:
        print e
    config.Exit()
          
def test_configuration():
    configuration = Configuration("config.ini")
    try:
        for i in range(10):
            configuration.iPAddress = "192.168.0." + str(i)
            time.sleep(5)
    except Exception, e:
        print e
    configuration.Exit()
    
if __name__ == "__main__":
    #test_configuration()
    test_getIPAddress()